Tanzania, August, 2019
leaflet() initialises a leaflet object m. Pipe this into addCircles() where we refer to the map_data dataframe and indicate which columns correspond to latitude and longitude.
m <- leaflet() %>% addCircles(data = map_data, lng = ~x, lat = ~y) m
Tiles give our data geographic context. addTiles() uses OpenStreetMap tiles, the default for leaflet
m %>% addTiles()
addProviderTiles()Allows us to use third party tiles (e.g. Terrain tiles from Stamen Design).
# m created using code from previous slides m <- leaflet() %>% addCircles(data=map_data, lng=~x, lat=~y)
Code options
addProviderTiles(map = m, provider = "Stamen.Terrain") addProviderTiles(map = m, provider = providers$Stamen.Terrain) m %>% addProviderTiles(provider = providers$Stamen.Terrain)
Try some options for provider tiles.
Hint: Use tab completion with addProviderTiles() to access options for arguments and available tiles.
Hint: providers$
addProviderTiles()m %>% addProviderTiles(providers$Esri.NatGeoWorldMap)
addProviderTiles()m %>% addProviderTiles(providers$Wikimedia)
addProviderTiles()m %>% addProviderTiles(providers$Esri.WorldTopoMap)
addProviderTiles()m %>% addProviderTiles(providers$Esri.WorldImagery)
addProviderTiles()m %>% addProviderTiles(providers$CartoDB.Positron)
addProviderTiles()https://github.com/leaflet-extras/leaflet-providers
Full list of free-to-use tiles with previews:
http://leaflet-extras.github.io/leaflet-providers/preview/index.html
Tile servers based on OpenStreetMap data with info on how to cite:
https://wiki.openstreetmap.org/wiki/Tile_servers
e.g. When using the tiles provider$CartoDB.Positron:
Figure 1. Map of virus cases in Tanzania during 2014. Cases coloured blue. Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL.
An interactive way for the user to get more info on data points
Easier to first store what we want to display as an object popupInfo
popupInfo <- paste("Date: ", map_data$date, "<br>",
"Species: ", map_data$species, "<br>",
"Age: ", map_data$age, "<br>",
sep = " ")
m <- leaflet() %>% addProviderTiles(providers$CartoDB.Positron)
Then, refer to popupInfo when using addCircles(), addCircleMarkers() etc.
m %>% addCircles(data = map_data, lng = ~x, lat = ~y, popup = popupInfo)
An interactive way for the user to get more info on data points
m %>% addCircles(data = map_data, lng = ~x, lat = ~y, popup = popupInfo)
Labels show up when hovered over, popups when the user clicks.
m %>% addCircles(data = map_data, lng = ~x, lat = ~y, popup = popupInfo,
label = ~species)
Circle markers (addCircleMarkers()) are similar to circles but radius in onscreen pixels is constant regardless of zoom level.
m %>% addCircleMarkers(data=map_data, lng=~x, lat=~y, popup = popupInfo, radius = 5,
stroke = T, weight = 3, color = "grey", opacity = 0.8,
fillColor = ifelse(map_data$species == "human",
"firebrick", "dodgerblue"), fillOpacity = 0.7)
With, stroke = F, weight, color, and opacity are irrelevant. Can also use radius = to size circle markers by species.
m %>% addCircleMarkers(data = map_data, lng = ~x, lat = ~y, popup = popupInfo, stroke = F,
radius = ifelse(map_data$species == "human", 8, 4),
fillColor = ifelse(map_data$species == "human", "firebrick", "dodgerblue"),
fillOpacity = 0.8)
On a map with many markers, it might make sense to cluster them using clusterOptions = markerClusterOptions() shown here with the default options for markerClusterOptions().
m %>% addMarkers(data = map_data, lng = ~x, lat = ~y, label = ~species, popup = popupInfo)
On a map with many markers, it might make sense to cluster them using clusterOptions = markerClusterOptions() shown here with the default options for markerClusterOptions().
m %>% addMarkers(data = map_data, lng = ~x, lat = ~y, label = ~species, popup = popupInfo,
clusterOptions = markerClusterOptions())
colorFactor()leaflet::colourFactor() creates a function (we're going to call it myPal) that is used as an argument when we use addCircles(), addCircleMarkers()etc.
# Choose 3 colours for our 3 groups
colour_pal <- c("gold", "forestgreen", "cornflowerblue")
myPal <- colorFactor(palette = colour_pal,
domain = c("Human", "Domestic", "Wildlife"))
Create a fresh version of m before we add coloured circles
m <- leaflet() %>% addProviderTiles(provider = providers$CartoDB.Positron)
colorFactor()myPal() takes species type variable as argument
m <- m %>% addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$species_type),
opacity = 0.9, popup = popupInfo)
m
m <- m %>% addLegend(position = "topright", title = "Species<br>type", pal = myPal,
values = map_data$species_type, opacity = 0.9)
m
Adapt the code from previous slides to produce a map with:
1) tiles showing topography
2) circles coloured by species (instead of species type)
3) a colour legend
4) a scale bar showing distance in kilometers (hint: ?addScaleBar).
Previous code:
colour_pal <- c("gold", "forestgreen", "cornflowerblue")
myPal <- colorFactor(colour_pal, domain = c("Human", "Domestic", "Wildlife"))
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$species_type), opacity = 0.9) %>%
addLegend(position = "topright", title = "Species<br>type", pal = myPal,
values = map_data$species_type, opacity = 0.9)
m
Part 1) tiles showing topography
m <- leaflet() %>% addProviderTiles(provider = providers$Esri.WorldPhysical) m %>% setView(lat = -6.4333, lng = 38.9, zoom = 5)
Part 2) circles coloured by species (instead of species type)
colour_pal <- c("gold", "forestgreen", "cornflowerblue", "red", "magenta")
myPal <- colorFactor(palette = colour_pal, domain = unique(map_data$species))
m <- m %>% addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$species), opacity = 0.9)
m
Part 2) circles coloured by species (instead of species type)
myPal <- colorFactor(palette = "Set2", domain = unique(map_data$species))
m <- m %>% addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$species), opacity = 0.9)
m
Part 3) a colour legend
m <- m %>% addLegend(position = "topright", title = "Species", pal = myPal,
values = map_data$species, opacity = 0.9)
m
Part 4) a scale bar showing distance in kilometers
m <- m %>% addScaleBar(position = "bottomleft",
options = scaleBarOptions(maxWidth = 200, # default = 100
imperial = FALSE))
m
First, need to convert date variable to a numeric value
head(map_data$date, 5)
## [1] "2014-01-01" "2014-01-01" "2014-01-02" "2014-01-03" "2014-01-04"
is.factor(map_data$date)
## [1] FALSE
# Use 2 functions from lubridate for conversion map_data$date_decimal <- lubridate::decimal_date(lubridate::ymd(map_data$date))
date_decimal is now a numeric variable
# Use 2 functions from lubridate for conversion map_data$date_decimal <- lubridate::decimal_date(lubridate::ymd(map_data$date)) head(map_data$date_decimal)
## [1] 2014.000 2014.000 2014.003 2014.005 2014.008 2014.014
is.numeric(map_data$date_decimal)
## [1] TRUE
Provide some colours and values of date to colorNumeric().
Either provide colours (palette = c("darkgreen", "green", "gold""))) or refer to a palette (palette = "Spectral").
dateRange <- c(2014, 2015) myPal <- colorNumeric(palette = "YlOrRd", domain = dateRange, reverse = T) # new leaflet widget m <- leaflet() %>% addProviderTiles(providers$CartoDB.Positron)
Add circles and a legend referring to myPal
m %>% addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$date_decimal), opacity = 0.9) %>%
addLegend(position = "topright", title = "Date",
pal = myPal, values = dateRange, opacity = 0.9,
labFormat = labelFormat(big.mark = "")) # removes a comma from 2,014
Adapt the code from previous slides to produce a map with:
1) Circles coloured by population density using the Spectral palette
2) and sized by population density (higher density = larger circles)
3) with colour legend and scale bar
If you get the solution, try colorBin() instead of colourNumeric()
Previous code to adapt:
dateRange <- c(2014, 2015)
myPal <- colorNumeric(palette = "YlOrRd", domain = dateRange)
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircles(data = map_data, lng = ~x, lat = ~y,
color = myPal(map_data$date_decimal), opacity = 0.9) %>%
addLegend(position = "topright", title = "Date",
pal = myPal, values = dateRange, opacity = 0.9,
labFormat = labelFormat(big.mark = ""))
m
Colouring by density:
densityRange <- c(0.1, 12000)
myPal <- colorNumeric(palette = "Spectral", domain = densityRange)
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircleMarkers(data = map_data, lng = ~x, lat = ~y, radius = 3,
stroke = F, fillColor = myPal(map_data$density), fillOpacity = 0.9) %>%
addLegend(position = "topright", title = "People<br>per km<sup>2</sup>",
pal = myPal, values = densityRange, opacity = 0.9) %>%
addScaleBar(position = "bottomleft")
Colouring by density:
m
and sizing by density, we add radius = ~log(density):
densityRange <- c(0.1, 12000)
myPal <- colorNumeric(palette = "Spectral", domain = densityRange)
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircleMarkers(data = map_data, lng = ~x, lat = ~y, radius = ~log(density),
stroke = F, fillColor = myPal(map_data$density), fillOpacity = 0.9) %>%
addLegend(position = "topright", title = "People<br>per km<sup>2</sup>",
pal = myPal, values = densityRange, opacity = 0.9) %>%
addScaleBar(position = "bottomleft")
and sizing by density, we add radius = ~log(density):
m
colorBin()With colorBin() we provide a vector of numbers
When we build up the leaflet widget, we just remove the log() functions as we no longer need to transform
myPal <- colorBin(palette = "Spectral", domain = map_data$density,
bins = c(0, 100, 250, 500, 750, 1000, 5000, Inf))
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircleMarkers(data = map_data, lng = ~x, lat = ~y, radius = ~log(density),
stroke = F, fillColor = myPal(map_data$density), fillOpacity = 0.9) %>%
addLegend(position = "topright", title = "Human<br>density",
pal = myPal, values = map_data$density, opacity = 0.9) %>%
addScaleBar(position = "bottomleft")
m
colourQuantile()myPal <- colorQuantile(palette = "Spectral", domain = map_data$density, n = 5)
leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addCircleMarkers(data = map_data, lng = ~x, lat = ~y, radius = ~log(density),
stroke = F, fillColor = myPal(map_data$density), fillOpacity = 0.9) %>%
addLegend(position = "topright", title = "Human<br>density",
pal = myPal, values = map_data$density, opacity = 0.9,
labFormat = labelFormat(big.mark = ""))
Load shape data and look at the structure
region_shp <- rgdal::readOGR(dsn = "data/TZ_Region_2012_density")
## OGR data source with driver: ESRI Shapefile ## Source: "/Users/wharvey/Documents/git/ShinyCourse/Day 2/slides/data/TZ_Region_2012_density", layer: "TZ_Region_2012.density" ## with 30 features ## It has 4 fields
head(region_shp)
## class : SpatialPolygonsDataFrame ## features : 6 ## extent : 31.03802, 39.85569, -6.976504, -1.027046 (xmin, xmax, ymin, ymax) ## coord. ref. : +proj=longlat +ellps=WGS84 +no_defs ## variables : 4 ## names : Region_Nam, Loc_ID, Loc_type, density ## min values : Kusini Pemba, Region-Kusini Pemba, Region, 94.02598 ## max values : Tanga, Region-Tanga, Region, 1002.31128
Use addPolygons() to add to leaflet map. label = determines what will be shown when cursor hovers above area.
m <- leaflet() %>% addProviderTiles(provider = providers$Esri.WorldShadedRelief)
m %>% addPolygons(data = region_shp, label = region_shp$Region_Nam,
color = "black", weight = 1, fillOpacity = 0.3, opacity = 0.6)
Use highlightOptions() to highlight shapes when cursor hovers above. bringToFront = TRUE avoids potential problems with order in which shapes are coded in shape file.
m %>% addPolygons(data = region_shp, label = region_shp$Region_Nam, fillColor = "gold",
color = "black", weight = 1, fillOpacity = 0.3, opacity = 0.6,
highlightOptions = highlightOptions(color = "white", weight = 3,
bringToFront = TRUE, opacity = 1))
Possible to use addPolylines() to add outlines only to leaflet map.
m %>% addPolylines(data = region_shp, color = "red", weight = 2, opacity = 1)
Choropleth: Map with areas shaded according to some numeric variable
(similar to heatmap but uses geographic boundaries)
myPal <- colorNumeric(palette = "YlOrRd", domain = region_shp$density)
m %>% addPolygons(data = region_shp, label = region_shp$Region_Nam,
fillColor = myPal(region_shp$density), fillOpacity = 0.7,
color = "white", opacity = 1, weight = 2)
Like before, we can log density to give a more informative map
myPal <- colorNumeric(palette = "YlOrRd", domain = log(region_shp$density))
m %>% addPolygons(data = region_shp, label = region_shp$Region_Nam,
fillColor = myPal(log(region_shp$density)), fillOpacity = 0.7,
color = "white", opacity = 1, weight = 2)
Adjust the choropleth code below to:
colorQuantile() instead of colorNumeric()m <- leaflet() %>% addProviderTiles(provider = providers$Esri.WorldShadedRelief)
myPal <- colorNumeric(palette = "YlOrRd", domain = log(region_shp$density))
m %>% addPolygons(data = region_shp, label = region_shp$density,
fillColor = myPal(log(region_shp$density)), fillOpacity = 0.7,
color = "white", opacity = 1, weight = 2)
Subset using map_data[map_data$species == "human",] or alternatively dplyr::select()
With colorQuantile(), we don't need to log density
Create a second colour palette to colour cases by date using colorNumeric().
human_data <- map_data[map_data$species == "human",]
myPal <- colorQuantile(palette = "YlOrRd", domain = region_shp$density)
myPal2 <- colorNumeric(palette = "viridis", domain = human_data$date_decimal)
m %>% addPolygons(data = region_shp, label = region_shp$density,
fillColor = myPal(region_shp$density), fillOpacity = 0.7,
color = "white", opacity = 1, weight = 2) %>%
addCircleMarkers(data = human_data, lat = ~y, lng = ~x, stroke = T,
fillColor = myPal2(human_data$date_decimal), fillOpacity = 0.7,
radius = 4, weight = 2, color = "black", opacity = 0.8)
human_data <- map_data[map_data$species == "human",]
myPal <- colorQuantile(palette = "YlOrRd", domain = region_shp$density)
myPal2 <- colorNumeric(palette = "viridis", domain = human_data$date_decimal)
m %>% addPolygons(data = region_shp, label = region_shp$density,
fillColor = myPal(region_shp$density), fillOpacity = 0.7,
color = "white", opacity = 1, weight = 2) %>%
addCircleMarkers(data = human_data, lat = ~y, lng = ~x,
fillColor = myPal2(human_data$date_decimal), fillOpacity = 0.7, radius = 4,
stroke = T, weight = 2, color = "black", opacity = 0.8)
labels <- sprintf("<strong>%s</strong><br/>%g people / km<sup>2</sup>",
region_shp$Region_Nam, round(region_shp$density, 0)) %>%
lapply(htmltools::HTML)
m %>% addPolygons(data = region_shp, fillColor = myPal(region_shp$density), label = labels,
weight = 2, opacity = 1, color = "white", fillOpacity = 0.7,
highlightOptions = highlightOptions(color = "black", weight = 3,
bringToFront = TRUE, opacity = 1))
Load data using raster() from the raster package
density <- raster::raster("data/HumanDensity/HumanPopulation.grd")
density
## class : RasterLayer ## dimensions : 259, 267, 69153 (nrow, ncol, ncell) ## resolution : 0.041665, 0.041665 (x, y) ## extent : 29.32649, 40.45105, -11.77259, -0.9813543 (xmin, xmax, ymin, ymax) ## coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 ## data source : /Users/wharvey/Documents/git/ShinyCourse/Day 2/slides/data/HumanDensity/HumanPopulation.grd ## names : TZA_popmap10adj_v2b ## values : 0, 29089.56 (min, max)
We can use the base plotting function plot() on the object we've created
plot(density)
Logging density will provide more texture to map
plot(log(density))
We use colorNumeric() again, as we did to colour by date earlier
colour_pal <- c("#FFFFCC", "#41B6C4", "#0C2C84")
raster::values(density) %>% range(na.rm = T)
## [1] 0.00 29089.56
myPal <- colorNumeric(palette = colour_pal, domain = c(0, 30000), na.color = "red") m <- leaflet() %>% addRasterImage(x = density, colors = myPal, opacity = 0.8)
m
Let's log density as we did before
raster::values(density) %>% range(na.rm = T) %>% log
## [1] -Inf 10.27813
myPal <- colorNumeric(palette = colour_pal, domain = c(-1.5, 11),
na.color = "transparent")
We have to exclude some data, as there are 0 density polygons and log(0) gives -infinity
sum(raster::values(density) > exp(-1.5), na.rm = T) / sum(raster::values(density) >= 0, na.rm = T)
## [1] 0.9975207
Plot the logged raster data with some tiles and a legend for the colour scheme.
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.Positron) %>%
addRasterImage(x = log(density), colors = myPal, opacity = 0.8) %>%
addLegend(pal = myPal, values = c(-1.5, 11), opacity = 0.8,
title = "Human<br>population<br>density")
## Warning in colors(.): Some values were outside the color scale and will be ## treated as NA
Instead of using addProviderTiles(provider = providers$CartoDB.Positron)
We order things here so that we plot
providers$CartoDB.PositronNoLabels)addRasterImage())providers$CartoDB.PositronOnlyLabels)m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.PositronNoLabels) %>%
addRasterImage(x = log(density), colors = myPal, opacity = 0.8) %>%
addProviderTiles(provider = providers$CartoDB.PositronOnlyLabels) %>%
addLegend(pal = myPal, values = c(-1.5, 11), opacity = 0.8,
title = "Human<br>population<br>density")
Adapt the code below to add region outlines that highlight when the cursor hovers an area. Use addPolygon() with fillOpacity = 0
colour_pal <- c("#FFFFCC", "#41B6C4", "#0C2C84")
myPal <- colorNumeric(palette = colour_pal, domain = c(-1.5, 11),
na.color = "transparent")
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.PositronNoLabels) %>%
addRasterImage(x = log(density), colors = myPal, opacity = 0.8) %>%
addProviderTiles(provider = providers$CartoDB.PositronOnlyLabels) %>%
addLegend(pal = myPal, values = c(-1.5, 11), opacity = 0.8,
title = "Human<br>population<br>density")
## Warning in colors(.): Some values were outside the color scale and will be ## treated as NA
m <- leaflet() %>%
addProviderTiles(provider = providers$CartoDB.PositronNoLabels) %>%
addRasterImage(x = log(density), colors = myPal, opacity = 0.8) %>%
addProviderTiles(provider = providers$CartoDB.PositronOnlyLabels) %>%
addLegend(pal = myPal, values = c(-1.5, 11), opacity = 0.8,
title = "Human<br>population<br>density") %>%
addPolygons(data = region_shp, label = region_shp$Region_Nam, fillOpacity = 0,
color = "black", weight = 1, opacity = 0.6,
highlightOptions = highlightOptions(color = "white", weight = 3,
bringToFront = TRUE, opacity = 1))
saveWidget() from the htmlwidgets package can save the map as a .html file.
You can email this to someone, add to a webpage etc.
There is no need for R to be running in the background.
saveWidget(widget = m, file = "filename.html")
mapshot() from the mapview package can save the map as a static image in either a .png, .pdf, or .jpeg file.
m <- m %>% setView(m, lat = -6.4333, lng = 38.9, zoom = 7.5) mapshot(m, file = "filename.png", width = 7, height = 6)
mapshot() from the mapview package can save the map as a static image in either a .png, .pdf, or .jpeg file.
Combine with setView() from the leaflet package to control where map view is centered and level of zoom
m <- m %>% setView(lat = -6.4333, lng = 38.9, zoom = 7.5) mapshot(m, file = "filename_zoom.png", width = 7, height = 6)